//
//  SLCACustomBufferController.m
//  Objective-C
//
//  Created by S_Line on 2018/7/3.
//  Copyright © 2018年 S_Line. All rights reserved.
//

#import "SLCACustomBufferController.h"

@interface SLCACustomBufferController ()<CAAnimationDelegate>

@property (weak, nonatomic) IBOutlet UIView *clockBackView;
@property (weak, nonatomic) IBOutlet UIImageView *hourHand;
@property (weak, nonatomic) IBOutlet UIImageView *minuteHand;
@property (weak, nonatomic) IBOutlet UIImageView *secondHand;
@property (nonatomic, weak) NSTimer *timer;

@property (weak, nonatomic) IBOutlet UIView *containerView;
@property (weak, nonatomic) UIImageView *ballImageView;

@end

@implementation SLCACustomBufferController

- (void)initNavBar {
    self.navTitle = @"CustomBuffer 自定义缓冲";
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initNavBar];
    
    [self initView];
}

- (void)initView {
    // adjust anchor point
    self.hourHand.layer.anchorPoint = CGPointMake(0.5, 0.8);
    self.minuteHand.layer.anchorPoint = CGPointMake(0.5, 0.8);
    self.secondHand.layer.anchorPoint = CGPointMake(0.5, 0.8);
    
    // start timer
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick) userInfo:nil repeats:true];
    
    // set initial hand positions
    [self updateHandsAnimated:false];
    
    UIImageView *ballImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 20, 20)];
    ballImageView.backgroundColor = [UIColor redColor];
    self.ballImageView = ballImageView;
    [self.containerView addSubview:ballImageView];
    
    [self animate];
}

- (void)tick {
    [self updateHandsAnimated:true];
}

- (void)updateHandsAnimated:(BOOL)animated {
    // convert time to hours, minutes and seconds
    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierChinese];
    NSUInteger units = NSCalendarUnitHour | NSCalendarUnitMinute | NSCalendarUnitSecond;
    NSDateComponents *components = [calendar components:units fromDate:[NSDate date]];
    CGFloat hourAngle = (components.hour / 12.0) * M_PI * 2.0;
    CGFloat minuteAngle = (components.minute / 60.0) * M_PI * 2.0;
    CGFloat secondAngle = (components.second / 60.0) * M_PI * 2.0;
    
    // rotate hands
    [self setAngle:hourAngle forHand:self.hourHand animated:animated];
    [self setAngle:minuteAngle forHand:self.minuteHand animated:animated];
    [self setAngle:secondAngle forHand:self.secondHand animated:animated];
}

- (void)setAngle:(CGFloat)angle forHand:(UIView *)handView animated:(BOOL)animated {
    // generate transform
    CATransform3D transform = CATransform3DMakeRotation(angle, 0, 0, 1);
    if (animated) {
        // create transform animation
        CABasicAnimation *animation = [CABasicAnimation animation];
        animation.keyPath = @"transform";
        animation.fromValue = [handView.layer.presentationLayer valueForKey:@"transform"];
        animation.toValue = [NSValue valueWithCATransform3D:transform];
        animation.duration = 0.5;
        animation.delegate = self;
        animation.timingFunction = [CAMediaTimingFunction functionWithControlPoints:1.0 :0.0 :0.75 :1.0];
        // apply animation
        handView.layer.transform = transform;
        [handView.layer addAnimation:animation forKey:nil];
    } else {
        // set transform directly
        handView.layer.transform = transform;
    }
}

float interpolate(float from, float to, float time) {
    return (to - from) * time + from;
}

- (id)interpolateFromValue:(id)fromValue toValue:(id)toValue time:(float)time {
    if ([fromValue isKindOfClass:[NSValue class]]) {
        //get type
        const char *type = [fromValue objCType];
        if (strcmp(type, @encode(CGPoint)) == 0) {
            CGPoint from = [fromValue CGPointValue];
            CGPoint to = [toValue CGPointValue];
            CGPoint result = CGPointMake(interpolate(from.x, to.x, time), interpolate(from.y, to.y, time));
            return [NSValue valueWithCGPoint:result];
        }
    }
    // provide safe default implementation
    return (time < 0.5) ? fromValue : toValue;
}

float bounceEaseOut(float t)
{
    if (t < 4/11.0) {
        return (121 * t * t)/16.0;
    } else if (t < 8/11.0) {
        return (363/40.0 * t * t) - (99/10.0 * t) + 17/5.0;
    } else if (t < 9/10.0) {
        return (4356/361.0 * t * t) - (35442/1805.0 * t) + 16061/1805.0;
    }
    return (54/5.0 * t * t) - (513/25.0 * t) + 268/25.0;
}

- (void)animate {
    // reset ball to top of containerView
    self.ballImageView.center = CGPointMake(150, 10);
    
    // set up animation parameters
    NSValue *fromValue = [NSValue valueWithCGPoint:CGPointMake(150, 10)];
    NSValue *toValue = [NSValue valueWithCGPoint:CGPointMake(150, kScreenWidth - 10)];
    CFTimeInterval duration = 1.0;
    // generate keyframes
    NSInteger numFrames = duration * 60;
    NSMutableArray *frames = [NSMutableArray new];
    for (int i = 0; i < numFrames; i++) {
        float time = 1 / (float)numFrames * i;
        // apply easing
        time = bounceEaseOut(time);
        // add keyframe
        [frames addObject:[self interpolateFromValue:fromValue toValue:toValue time:time]];
    }
    
    // create keyframe animation
    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
    animation.keyPath = @"position";
    animation.duration = 1.0;
    animation.delegate = self;
    animation.values = frames;
    
    // apply animation
    [self.ballImageView.layer addAnimation:animation forKey:nil];
    
//    // reset ball to top of screen
//    self.ballImageView.center = CGPointMake(150, 10);
//    // create keyframe animation
//    CAKeyframeAnimation *animation = [CAKeyframeAnimation animation];
//    animation.keyPath = @"position";
//    animation.duration = 1.0;
//    animation.delegate = self;
//    animation.values = @[
//                         [NSValue valueWithCGPoint:CGPointMake(150, 32)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 140)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 220)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 268)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 250)],
//                         [NSValue valueWithCGPoint:CGPointMake(150, 268)]
//                         ];
//    animation.timingFunctions = @[
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseOut],
//                                  [CAMediaTimingFunction functionWithName: kCAMediaTimingFunctionEaseIn]
//                                  ];
//    animation.keyTimes = @[@0.0, @0.3, @0.5, @0.7, @0.8, @0.9, @0.95, @1.0];
//    // apply animation
//    self.ballImageView.layer.position = CGPointMake(150, kScreenWidth - 10);
//    [self.ballImageView.layer addAnimation:animation forKey:nil];
}

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self animate];
}

@end
